package com.weed.logging;



import com.weed.core.WeedException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
/**
 *<p>日志类读取顺序：</p>
 *<p>    1、从系统的中获取System.getProperty(String name)获取。</p>
 *<p>    1、从SERVICE_ID读取。META-INF/services/com.weed.loggin。</p>
 *<p>    2、从weed-loggin.properties中读取。可以是War的Meta-inf下或Jar的Meta-</p>
 *<p>     inf下。这个不推荐使用，有可能出J2EE服务器中因为ClassLoader的策略而加载到</p>
 *<p>     其他的应用。</p>
 *<p>    3、默认读取LogFactoryImpl。</p>
 * @author Jason zhuo
 */
public abstract class LogFactory {

    public static final String FACTORY_PROPERTY =
            "com.weed.logging.LogFactory";
    public static final String FACTORY_DEFAULT =
            "com.weed.logging.impl.LogFactoryImpl";
    public static final String FACTORY_PROPERTIES =
            "weed-logging.properties";
    protected static final String SERVICE_ID =
            "META-INF/services/com.weed.logging.LogFactory";

    protected LogFactory() {
    }
    /**
     * 从属性文件中获得值。
     * @param name 根据名称从缓存中取得对应的值。
     * @return
     */
    public abstract Object getAttribute(String name);
    /**
     * 获得所有属性文件中配置的值。
     * @return
     */
    public abstract String[] getAttributeNames();
    /**
      * 根据属性文件的健获得对应配置的值。
      * @param name
      */
    public abstract void removeAttribute(String name);
    /**
     * 设定从属性文件中读取的健以及对应的值。
     * @param name
     * @param value
     */
    public abstract void setAttribute(String name, Object value);
    /**
     * 根据对象(clazz)初始化生成一个Log对象。
     * @param clazz
     * @return
     * @throws WeedException
     */
    public abstract Log getInstance(Class clazz)
            throws WeedException;
  /**
     * 根据对象名称(name)初始化生成一个Log对象。
     * @param name
     * @return
     * @throws WeedException
     */
    public abstract Log getInstance(String name)
            throws WeedException;
    /**
     * 释放所有资源。
     */
    public abstract void release();

    /**用于缓冲ClassLoader.*/
    protected static Hashtable factories = new Hashtable();
    /**
     * 获得日志工厂。
     * @return
     * @throws WeedException
     */
    public static LogFactory getFactory() throws WeedException {
        // Identify the class loader we will be using
        ClassLoader contextClassLoader =
                (ClassLoader) AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        return getContextClassLoader();
                    }
                });
        //返回之前注册的这类加载器
        LogFactory factory = getCachedFactory(contextClassLoader);
        if (factory != null) {
            return factory;
        }
        // First, try the system property
        try {
            String factoryClass = System.getProperty(FACTORY_PROPERTY);
            if (factoryClass != null) {
                factory = newFactory(factoryClass, contextClassLoader);
            }
        } catch (SecurityException e) {
            ;  // ignore
        }

        if (factory == null) {
            try {
                InputStream is = getResourceAsStream(contextClassLoader,
                        SERVICE_ID);

                if (is != null) {
                    // This code is needed by EBCDIC and other strange systems.
                    // It's a fix for bugs reported in xerces
                    BufferedReader rd;
                    try {
                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                    } catch (java.io.UnsupportedEncodingException e) {
                        rd = new BufferedReader(new InputStreamReader(is));
                    }

                    String factoryClassName = rd.readLine();
                    rd.close();

                    if (factoryClassName != null &&
                            !"".equals(factoryClassName)) {

                        factory = newFactory(factoryClassName, contextClassLoader);
                    }
                }
            } catch (Exception ex) {
                ;
            }
        }
        //需要时再读取。
        Properties props = null;
        try {
            InputStream stream = getResourceAsStream(contextClassLoader,
                    FACTORY_PROPERTIES);

            if (stream != null) {
                props = new Properties();
                props.load(stream);
                stream.close();
            }
        } catch (IOException e) {
        } catch (SecurityException e) {
        }

        if (factory == null && props != null) {
            String factoryClass = props.getProperty(FACTORY_PROPERTY);
            if (factoryClass != null) {
                factory = newFactory(factoryClass, contextClassLoader);
            }
        }
        //设用默认的实现。
        if (factory == null) {
            factory = newFactory(FACTORY_DEFAULT, LogFactory.class.getClassLoader());
        }

        if (factory != null) {
            /**
             * Always cache using context class loader.
             */
            cacheFactory(contextClassLoader, factory);

            if (props != null) {
                Enumeration names = props.propertyNames();
                while (names.hasMoreElements()) {
                    String name = (String) names.nextElement();
                    String value = props.getProperty(name);
                    factory.setAttribute(name, value);
                }
            }
        }
        return factory;
    }

    public static Log getLog(Class clazz) {
        return (getFactory().getInstance(clazz));
    }

    /**
     * 从指定的工厂中获得对应的日志处理对象。
     * @param name
     * @return
     * @throws WeedException
     */
    public static Log getLog(String name)
            throws WeedException {
       return (getFactory().getInstance(name));
    }

    /**
     * 释放任何内部引用了。
     * @param classLoader需要释放的日志工厂。
     */
    public static void release(ClassLoader classLoader) {
        synchronized (factories) {
            LogFactory factory = (LogFactory) factories.get(classLoader);
            if (factory != null) {
                factory.release();
                factories.remove(classLoader);
            }
        }

    }

     /**
      * 释放所有的日志工厂。
      */
    public static void releaseAll() {
        synchronized (factories) {
            Enumeration elements = factories.elements();
            while (elements.hasMoreElements()) {
                LogFactory element = (LogFactory) elements.nextElement();
                element.release();
            }
            factories.clear();
        }

    }
    /**
     * 获得上下文环境的ClassLoader.
     * @return
     * @throws WeedException
     */
    protected static ClassLoader getContextClassLoader()
            throws WeedException {
        ClassLoader classLoader = null;
        try {
            // 兼容之前的版本。
            Method method = Thread.class.getMethod("getContextClassLoader", null);
            try {
                classLoader = (ClassLoader) method.invoke(Thread.currentThread(), null);
            } catch (IllegalAccessException e) {
                throw new WeedException("Unexpected IllegalAccessException", e);
            } catch (InvocationTargetException e) {
                if (e.getTargetException() instanceof SecurityException) {
                    ; //安全异常就略过。
                } else {
                    throw new WeedException("Unexpected InvocationTargetException", e.getTargetException());
                }
            }
        } catch (NoSuchMethodException e) {
            classLoader = LogFactory.class.getClassLoader();
        }
        return classLoader;
    }

    /**
     * 确认缓存工厂是否存在指定的LogFactory.
     */
    private static LogFactory getCachedFactory(ClassLoader contextClassLoader) {
        LogFactory factory = null;
        if (contextClassLoader != null) {
            factory = (LogFactory) factories.get(contextClassLoader);
        }
        return factory;
    }
    /**
     * 将classLoader添加到缓存工厂。
     * @param classLoader
     * @param factory
     */
    private static void cacheFactory(ClassLoader classLoader, LogFactory factory) {
        if (classLoader != null && factory != null) {
            factories.put(classLoader, factory);
        }
    }
    /**
     * 新增一缓存工厂。
     * @param factoryClass
     * @param classLoader
     * @return
     * @throws WeedException
     */
    protected static LogFactory newFactory(final String factoryClass,
            final ClassLoader classLoader)
            throws WeedException {
        Object result = AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        Class logFactoryClass = null;
                        try {
                            if (classLoader != null) {
                                try {
                                    logFactoryClass = classLoader.loadClass(factoryClass);
                                    return (LogFactory) logFactoryClass.newInstance();

                                } catch (ClassNotFoundException ex) {
                                    if (classLoader == LogFactory.class.getClassLoader()) {
                                        throw ex;
                                    }
                                } catch (NoClassDefFoundError e) {
                                    if (classLoader == LogFactory.class.getClassLoader()) {
                                        // Nothing more to try, onwards.
                                        throw e;
                                    }

                                } catch (ClassCastException e) {
                                        if (classLoader == LogFactory.class.getClassLoader()) {
                                        throw e;
                                    }
                                }
                            }
                            logFactoryClass = Class.forName(factoryClass);
                            return (LogFactory) logFactoryClass.newInstance();
                        } catch (Exception e) {
                            if (logFactoryClass != null && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
                                return new WeedException("The chosen LogFactory implementation does not extend LogFactory." + " Please check your configuration.",e);
                            }
                            return new WeedException(e);
                        }
                    }
                });
        if (result instanceof WeedException) {
            throw (WeedException) result;
        }
      return (LogFactory) result;
    }
    /**
     * 获得资源流。
     * @param loader
     * @param name
     * @return
     */
    private static InputStream getResourceAsStream(final ClassLoader loader,
            final String name) {
        return (InputStream) AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        if (loader != null) {
                            return loader.getResourceAsStream(name);
                        } else {
                            return ClassLoader.getSystemResourceAsStream(name);
                        }
                    }
                });
    }


}
